/*
 * cradlecc.c
 *
 *  Created on: Nov 8, 2010
 *      Author: cpy.prefers.you@gmail.com
 */

#include "headers.h"

static int RightParenthesesIgnore = 0;

static int MatchToken(char *ExpectedStr)
{
    if (!strcmp(ExpectedStr, Token0->Name))
        Scan();
    else
        Expected(ExpectedStr);
    return 0;
}

static void SetStorageClass(Symbol_T *Symbol, const Symbol_T *StorageClass)
{
    Symbol->StorageClass = (Symbol_T*)StorageClass;
    return;
}

static void SetModifierOfLen(Symbol_T*Symbol, const Symbol_T *ModifierOfLen)
{
    Symbol->ModifierOfLen= (Symbol_T*)ModifierOfLen;
    return;
}

static void SetModifierOfSign(Symbol_T *Symbol, const Symbol_T *ModifierOfSign)
{
    Symbol->ModifierOfSign= (Symbol_T*)ModifierOfSign;
    return;
}

static void SetQualifier(Symbol_T *Symbol, const Symbol_T *Token)
{
    if (Symbol->Qualifier != Token)
        Symbol->Qualifier = LookupName("const&volatile");
    else
        Symbol->Qualifier = (Symbol_T*)Token;
    return;
}

static void SetDataType(Symbol_T *Symbol, const Symbol_T *DataType)
{
    Symbol->DataType = (Symbol_T*)DataType;
    return;
}

static void AssignDeclarationHead(Symbol_T *Symbol, const Symbol_T *Token)
{
    switch (Token->KeywordType)
    {
        case KEYWORD_STORAGE_CLASS:
            if (Symbol->StorageClass)
            {
                printf("Symbol: %s, Token: %s\n", Symbol->Name, Token->Name);
                if (Symbol->StorageClass == Token)
                    Abort("duplicate '%s'", Symbol->StorageClass->Name);
                else
                    Abort("multiple storage classes in declaration");
            }
            else
                SetStorageClass(Symbol, Token);
            break;

        case KEYWORD_MODIFIER_LEN:
            if (Symbol->ModifierOfLen)
            {
                if (Symbol->ModifierOfLen == Token)
                {
                    if (Symbol->ModifierOfLen == LookupName("long"))
                        SetModifierOfLen(Symbol, LookupName("long long"));
                    else
                        Abort("duplicat '%s'", Symbol->ModifierOfLen->Name);
                }
                else
                    Abort("both %s and %s specified", Symbol->ModifierOfLen->Name, Token->Name);
            }
            else
                SetModifierOfLen(Symbol, Token);
            break;

        case KEYWORD_MODIFIER_SIGN:
            if (Symbol->ModifierOfSign)
            {
                if (Symbol->ModifierOfSign == Token)
                    Abort("duplicate '%s'", Symbol->ModifierOfSign->Name);
                else
                    Abort("both %s and %s specified", Symbol->ModifierOfSign->Name, Token->Name);
            }
            else
                SetModifierOfSign(Symbol, Token);
            break;

        case KEYWORD_QUALIFIER:
            SetQualifier(Symbol, Token);
            break;

        case KEYWORD_DATA_TYPE:
            if (Symbol->DataType)
                Abort("two or more data types in declaration");
            else
                SetDataType(Symbol, Token);
            break;
        default:
            break;
    }
    return;
}

static int Declaration()
{
    Symbol_T *Symbol = calloc(1, sizeof(Symbol_T));

    if (IS_DECLARATION_HEAD(Token0->KeywordType))
        while (IS_DECLARATION_HEAD(Token0->KeywordType))
        {
            AssignDeclarationHead(Symbol, Token0);
            Scan();
        }

    if (IS_DECLARATION_BODY(Token0->Type))
        /* supports non-pointer variables only. to be extended. */
        while (IS_DECLARATION_BODY(Token0->Type))
        {
            while (Token0 == LookupName("*"))
            {
                Symbol->PointerLevel++;
                Scan();
            }

            Symbol->Name = Token0->Name;
            Scan();

            if (Token0 == LookupName("["))
            {
                //+ SetArrayProperty(Symbol);

            }

            if (Token0 == LookupName("="))
            {
                //+ SetInitialValue(Symbol);
            }

            //+ CheckDeclarationHead(Symbol);
            //+ CheckPreviousDefinition(Symbol, LookupName(Token->Name));

            InsertSymbol(Symbol);
            //+ Symbol->ArrayProperty = 0;
            Symbol->PointerLevel = 0;

            if (Token0->Type == TYPE_COMMA)
                MatchToken(Token0->Name);
            else if (Token0->Type == TYPE_SEMICOLON)
            {
                MatchToken(Token0->Name);
                break;
            }
            else
                Abort("syntax error before \'%s\'", Token0->Name);
        }
    else if (Token0->Type == TYPE_SEMICOLON)
        Warning("empty declaration");
    else
        Abort("syntax error before \'%s\'", Token0->Name);
    return 0;
}


/* parse and translate an identifier */
int Identifier()
{
    char *Name = Token0->Name;
    Scan();
    if (Token0->Name[0] == '(')
    {
        MatchToken("(");
        MatchToken(")");
        GENERATE_CODE("CALL %s", Name);
    }
    else
        GENERATE_CODE("PUSH %s", Name);
    return 0;
}

/* parse and translate an assignment statement */
int Assignment(char *Identifier)
{
    if (Token1->Name[0] == '=')
    {
        Scan();
        MatchToken("=");
        Expression();
        GENERATE_CODE("POP dword [%s]", Identifier);
    }
    else
    {
        /* to rewrite
        int i;
        for (i = 0; Token1->Name[i] != '='; i++);
        Token1->Name[i] = 0;
        Token1->Type = OpType(Token1->Name);

        Symbol_T *TokenBufferTemp;
        TokenBufferTemp = malloc(sizeof(Symbol_T));
        TokenBufferTemp->Name = "(";
        TokenBufferTemp->Type = TYPE_UNKNOWN;
        PutTokenToCache(TokenBufferTemp);

        RightParenthesesIgnore++;

        Expression();
        GENERATE_CODE("POP dword [%s]", Identifier);
        */
    }
    GENERATE_CODE("PUSH dword [%s]", Identifier);
    return 0;
}

/* parse and translate an expression */
int Expression()
{
    while (Token0->Type == TYPE_IDENTIFIER && Token1->Type == TYPE_ASSIGNOP)
    {
        char *Identifier = Token0->Name;
        Assignment(Identifier);
    }
    FlushOutputBufferToQueue();
    Operand(MAX_PRECEDENCE_LEVEL);
    return 0;
}

/* long & ugly code to perform an operation */
int PerformOperation(Symbol_T *Operation)
{
    char *Label0, *Label1;
    switch(Operation->Type)
    {
        case TYPE_MULOP:
            GENERATE_CODE("POP ebx");
            GENERATE_CODE("POP eax");
            GENERATE_CODE("MOV edx, 0");
            switch(Operation->Name[0])
            {
                case '*':
                    GENERATE_CODE("MUL ebx");
                    GENERATE_CODE("PUSH eax");
                    break;
                case '/':
                    GENERATE_CODE("DIV ebx");
                    GENERATE_CODE("PUSH eax");
                    break;
                case '%':
                    GENERATE_CODE("DIV ebx");
                    GENERATE_CODE("PUSH edx");
                    break;
                default:
                    break;
            }
            break;
        case TYPE_ADDOP:
            GENERATE_CODE("POP ebx");
            GENERATE_CODE("POP eax");
            switch(Operation->Name[0])
            {
                case '+':
                    GENERATE_CODE("ADD eax, abx");
                    break;
                case '-':
                    GENERATE_CODE("SUB eax, ebx");
                    break;
                default:
                    break;
            }
            GENERATE_CODE("PUSH eax");
            break;
        case TYPE_SHIFTOP:
            GENERATE_CODE("POP ebx");
            GENERATE_CODE("POP eax");
            switch(Operation->Name[0])
            {
                case '<':
                    GENERATE_CODE("SHL eax, abx");
                    break;
                case '>':
                    GENERATE_CODE("SHR eax, ebx");
                    break;
                default:
                    break;
            }
            GENERATE_CODE("PUSH eax");
            break;
        case TYPE_INEQUAL_COMPOP:
        case TYPE_EQUAL_COMPOP:
            GENERATE_CODE("POP ebx");
            GENERATE_CODE("POP eax");
            GENERATE_CODE("CMP eax, ebx");
            Label0 = NewLabel(&LabelCount);
            Label1 = NewLabel(&LabelCount);
            switch(Operation->Name[0])
            {
                case '<':
                    GENERATE_CODE("%s %s", Operation->Name[1] == '=' ? "JLE" : "JL", Label0);
                    break;
                case '>':
                    GENERATE_CODE("%s %s", Operation->Name[1] == '=' ? "JGE" : "JG", Label0);
                    break;
                case '=':
                    GENERATE_CODE("JE %s", Label0);
                    break;
                case '!':
                    GENERATE_CODE("JNE %s", Label0);
                    break;
                default:
                    break;
            }
            GENERATE_CODE("PUSH 0");
            GENERATE_CODE("JMP %s", Label1);
            GENERATE_CODE("%s:", Label0);
            GENERATE_CODE("PUSH 1");
            GENERATE_CODE("%s:", Label1);
            break;
        case TYPE_BIT_AND:
        case TYPE_BIT_XOR:
        case TYPE_BIT_OR:
            GENERATE_CODE("POP ebx");
            GENERATE_CODE("POP eax");
            switch(Operation->Type)
            {
                case TYPE_BIT_AND:
                    GENERATE_CODE("AND eax, ebx");
                    break;
                case TYPE_BIT_XOR:
                    GENERATE_CODE("XOR eax, ebx");
                    break;
                case TYPE_BIT_OR:
                    GENERATE_CODE("OR eax, ebx");
                    break;
                default:
                    break;
            }
            GENERATE_CODE("PUSH eax");
            break;
        case TYPE_LOGIC_AND:
            Label0 = NewLabel(&LabelCount);
            Label1 = NewLabel(&LabelCount);
            GENERATE_CODE("POP eax");
            GENERATE_CODE("CMP eax, 0");
            GENERATE_CODE("JE %s", Label0);
            GENERATE_CODE("POP eax");
            GENERATE_CODE("CMP eax, 0");
            GENERATE_CODE("JE %s", Label0);
            GENERATE_CODE("PUSH 1");
            GENERATE_CODE("JMP %s", Label1);
            GENERATE_CODE("%s:", Label0);
            GENERATE_CODE("PUSH 0");
            GENERATE_CODE("%s:", Label1);
            break;

        case TYPE_LOGIC_OR:
            Label0 = NewLabel(&LabelCount);
            Label1 = NewLabel(&LabelCount);
            GENERATE_CODE("POP eax");
            GENERATE_CODE("CMP eax, 0");
            GENERATE_CODE("JNE %s", Label0);
            GENERATE_CODE("POP eax");
            GENERATE_CODE("CMP eax, 0");
            GENERATE_CODE("JNE %s", Label0);
            GENERATE_CODE("PUSH 0");
            GENERATE_CODE("JMP %s", Label1);
            GENERATE_CODE("%s:", Label0);
            GENERATE_CODE("PUSH 1");
            GENERATE_CODE("%s:", Label1);
            break;

        default:
            break;
    }
    return 0;
}

/* parse and translate an operand */
int Operand(int PrecedenceLevel)
{
    if (PrecedenceLevel == MIN_PRECEDENCE_LEVEL)
    {
        if (Token0->Name[0] == '(')
        {
            MatchToken("(");
            if (RightParenthesesIgnore)
                RightParenthesesIgnore--;
            Expression();
            if (RightParenthesesIgnore)
                MatchToken(")");
        }
        else if (Token0->Type == TYPE_KEYWORD)
            Abort("Wrong keyword use");
        else if (Token0->Type == TYPE_IDENTIFIER)
            Identifier();
        else if (Token0->Type == TYPE_NUMBER)
        {
            GENERATE_CODE("PUSH %s", Token0->Name);
            Scan();
        }
        /*else if (Token0->Type != TYPE_SEMICOLON)
        {
            printf("%d %d %d %s\n", Token0->Type, Token0->Name[0], Token0->Name[1], Token0->Name);
            Expected("Operand");
        }*/
    }
    else
    {
        if (PrecedenceLevel < TYPE_ADDOP && Token0->Type == TYPE_ADDOP)
            GENERATE_CODE("PUSH 0");
        else if (Token0->Type == TYPE_INCOP && Token1->Type == TYPE_IDENTIFIER)
        {
            GENERATE_CODE("%s [%s]", Token0->Name[0] == '+' ? "INC" : "DEC", Token1->Name);
            Scan();
        }
        else if (Token0->Type == TYPE_IDENTIFIER && Token1->Type == TYPE_INCOP)
        {
            OutputToBuffer = 1;
            GENERATE_CODE("%s [%s]", Token0->Name[0] == '+' ? "INC" : "DEC", Token1->Name);
            OutputToBuffer = 0;
            PutTokenToCache(Token0);
            Scan();
            Scan();
        }
        else if (Token0->Type == TYPE_LOGIC_NOT)
        {
        }
        else
            Operand(PrecedenceLevel - 1);
        while (Token0->Type == PrecedenceLevel)
        {
            Symbol_T *Operation = Token0;
            Scan();
            Operand(PrecedenceLevel - 1);
            PerformOperation(Operation);
        }
    }
    return 0;
}

/**/

/* parse and translate an if(-else) construct */
int If()
{
    MatchToken("if");
    MatchToken("(");
    Expression();
    MatchToken(")");
    char *Label1, *Label2;
    Label1 = NewLabel(&LabelCount);
    Label2 = NewLabel(&LabelCount);
    GENERATE_CODE("POP eax");
    GENERATE_CODE("JZ %s", Label1);
    Statement();
    GENERATE_CODE("JMP %s", Label2);
    GENERATE_CODE("%s:", Label1);
    if (!strcmp(Token0->Name, "else"))
    {
        MatchToken("else");
        Statement();
    }
    GENERATE_CODE("%s:", Label2);
    return 0;
}

/* parse and translate a while construct */
int While()
{
    char *Label1, *Label2;
    Label1 = NewLabel(&LabelCount);
    Label2 = NewLabel(&LabelCount);
    MatchToken("while");
    MatchToken("(");
    GENERATE_CODE("%s:", Label1);
    Expression();
    MatchToken(")");
    GENERATE_CODE("JZ %s", Label2);
    Statement();
    GENERATE_CODE("JMP %s", Label1);
    GENERATE_CODE("%s:", Label2);
    return 0;
}

/* parse and translate a do-while construct */
int Do()
{
    MatchToken("do");
    char *Label;
    Label = NewLabel(&LabelCount);
    GENERATE_CODE("%s:", Label);
    Statement();
    MatchToken("(");
    Expression();
    MatchToken(")");
    GENERATE_CODE("JNZ %s", Label);
    return 0;
}

/* parse and translate a for construct */
int For()
{
    char *Label0, *Label1;
    Label0 = NewLabel(&LabelCount);
    Label1 = NewLabel(&LabelCount);

    MatchToken("for");
    MatchToken("(");
    Expression();
    MatchToken(";");
    GENERATE_CODE("%s:", Label0);
    Expression();
    GENERATE_CODE("JZ %s", Label1);
    MatchToken(";");
    OutputToBuffer = 1;
    Expression();
    OutputToBuffer = 0;
    MatchToken(")");
    Statement();
    FlushOutputBufferToQueue();
    GENERATE_CODE("JMP %s", Label0);
    GENERATE_CODE("%s:", Label1);
    return 0;
}

/* parse and translate a block */
int Block()
{
    MatchToken("{");
    while (Token0->Type != TYPE_RIGHT_BRACE)
    {
        Statement();
    }
    MatchToken("}");
    return 0;
}

/* parse and translate a statement */
int Statement()
{
    if (Token0->Type == TYPE_LEFT_BRACE)
        Block();
    else if (Token0->Type == TYPE_SEMICOLON)
        MatchToken(";");
    else if (Token0->Type == TYPE_KEYWORD)
    {
        if (!strcmp(Token0->Name, "if"))
            If();
        else if (!strcmp(Token0->Name, "while"))
            While();
        else if (!strcmp(Token0->Name, "do"))
            Do();
        else if (!strcmp(Token0->Name, "for"))
            For();
        else
            Abort("Wrong keyword usage");
    }
    else
    {
        Expression();
        MatchToken(";");
    }
    FlushOutputQueueToStream();
    return 0;
}

/* parse and translate a program */
int Program()
{
    while (!(Token0->Type == EOF))
        Declaration();
    return 0;
}

int main()
{
    #define DEBUG_MODE_2

    #ifdef DEBUG_MODE_1
    SymbolTableInit();
    LexerInit();
    while (1)
    {
        if (Token0->Type == EOF)
            return 0;
        printf("0x%p\t%ui\t%s\n", Token0, Token0->Type, Token0->Name);
        Scan();
    }
    return 0;
    #endif

    #ifdef DEBUG_MODE_2
    SymbolTableInit();
    LexerInit();
    Program();
    PrintAll();
    return 0;
    #endif

    return 0;
}
